home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / c / splay.com / SPLAY.PAS < prev   
Encoding:
Pascal/Delphi Source File  |  1988-08-16  |  9.5 KB  |  361 lines

  1. {*****************************************************************************
  2.   Compress and decompress files using the "splay tree" technique.
  3.   Based on an article by Douglas W. Jones, "Application of Splay Trees to
  4.   Data Compression", in Communications of the ACM, August 1988, page 996.
  5.  
  6.   This is a method somewhat similar to Huffman encoding (SQZ), but which is
  7.   locally adaptive. It therefore requires only a single pass over the
  8.   uncompressed file, and does not require storage of a code tree with the
  9.   compressed file. It is characterized by code simplicity and low data
  10.   overhead. Compression efficiency is not as good as recent ARC
  11.   implementations, especially for large files. However, for small files, the
  12.   efficiency of SPLAY approaches that of ARC's squashing technique.
  13.  
  14.   Usage:
  15.     SPLAY [/X] Infile Outfile
  16.  
  17.     when /X is not specified, Infile is compressed and written to OutFile.
  18.     when /X is specified, InFile must be a file previously compressed by
  19.     SPLAY, and OutFile will contain the expanded text.
  20.  
  21.     SPLAY will prompt for input if none is given on the command line.
  22.  
  23.   Caution! This program has very little error checking. It is primarily
  24.   intended as a demonstration of the technique. In particular, SPLAY will
  25.   overwrite OutFile without warning. Speed of SPLAY could be improved
  26.   enormously by writing the inner level bit-processing loops in assembler.
  27.  
  28.   Implemented on the IBM PC by
  29.     Kim Kokkonen
  30.     TurboPower Software
  31.     [72457,2131]
  32.     8/16/88
  33. *****************************************************************************}
  34.  
  35. {$R-,S-,I+}
  36.  
  37. program SplayCompress;
  38.  
  39. const
  40.   BufSize = 16384;                {Size of input and output buffers}
  41.   Sig = $FF02AA55;                {Arbitrary signature denotes compressed file}
  42.  
  43.   MaxChar = 256;                  {Ordinal of highest character}
  44.   EofChar = 256;                  {Used to mark end of compressed file}
  45.   PredMax = 255;                  {MaxChar-1}
  46.   TwiceMax = 512;                 {2*MaxChar}
  47.   Root = 0;                       {Index of root node}
  48.  
  49.   {Used to pack and unpack bits and bytes}
  50.   BitMask : array[0..7] of Byte = (1, 2, 4, 8, 16, 32, 64, 128);
  51.  
  52. type
  53.   FileHeader =
  54.     record
  55.       Signature : LongInt;
  56.       {Put any other info here, like the original file name or date}
  57.     end;
  58.  
  59.   BufferArray = array[1..BufSize] of Byte;
  60.  
  61.   CodeType = 0..MaxChar;          {Has size of Word}
  62.   UpIndex = 0..PredMax;           {Has size of Byte}
  63.   DownIndex = 0..TwiceMax;        {Has size of Word}
  64.   TreeDownArray = array[UpIndex] of DownIndex;
  65.   TreeUpArray = array[DownIndex] of UpIndex;
  66.  
  67. var
  68.   InBuffer : BufferArray;         {Input file buffer}
  69.   OutBuffer : BufferArray;        {Output file buffer}
  70.   InName : string[79];            {Input file name}
  71.   OutName : string[79];           {Output file name}
  72.   CompStr : string[3];            {Response from Expand? prompt}
  73.   InF : file;                     {Input file}
  74.   OutF : file;                    {Output file}
  75.  
  76.   Left, Right : TreeDownArray;    {Child branches of code tree}
  77.   Up : TreeUpArray;               {Parent branches of code tree}
  78.   CompressFlag : Boolean;         {True to compress file}
  79.   BitPos : Byte;                  {Current bit in byte}
  80.   InByte : CodeType;              {Current input byte}
  81.   OutByte : CodeType;             {Current output byte}
  82.   InSize : Word;                  {Current chars in input buffer}
  83.   OutSize : Word;                 {Current chars in output buffer}
  84.   Index : Word;                   {General purpose index}
  85.  
  86.   procedure InitializeSplay;
  87.     {-Initialize the splay tree - as a balanced tree}
  88.   var
  89.     I : DownIndex;
  90.     J : UpIndex;
  91.     K : DownIndex;
  92.   begin
  93.     for I := 1 to TwiceMax do
  94.       Up[I] := (I-1) shr 1;
  95.     for J := 0 to PredMax do begin
  96.       K := (J+1) shl 1;
  97.       Left[J] := K-1;
  98.       Right[J] := K;
  99.     end;
  100.   end;
  101.  
  102.   procedure Splay(Plain : CodeType);
  103.     {-Rearrange the splay tree for each succeeding character}
  104.   var
  105.     A, B : DownIndex;
  106.     C, D : UpIndex;
  107.   begin
  108.     A := Plain+MaxChar;
  109.     repeat
  110.       {Walk up the tree semi-rotating pairs}
  111.       C := Up[A];
  112.       if C <> Root then begin
  113.         {A pair remains}
  114.         D := Up[C];
  115.  
  116.         {Exchange children of pair}
  117.         B := Left[D];
  118.         if C = B then begin
  119.           B := Right[D];
  120.           Right[D] := A;
  121.         end else
  122.           Left[D] := A;
  123.         if A = Left[C] then
  124.           Left[C] := B
  125.         else
  126.           Right[C] := B;
  127.  
  128.         Up[A] := D;
  129.         Up[B] := C;
  130.         A := D;
  131.  
  132.       end else
  133.         {Handle odd node at end}
  134.         A := C;
  135.  
  136.     until A = Root;
  137.   end;
  138.  
  139.   procedure FlushOutBuffer;
  140.     {-Flush output buffer and reset}
  141.   begin
  142.     if OutSize > 0 then begin
  143.       BlockWrite(OutF, OutBuffer, OutSize);
  144.       OutSize := 0;
  145.     end;
  146.   end;
  147.  
  148.   procedure WriteByte;
  149.     {-Output byte in OutByte}
  150.   begin
  151.     if OutSize = BufSize then
  152.       FlushOutBuffer;
  153.     Inc(OutSize);
  154.     OutBuffer[OutSize] := OutByte;
  155.   end;
  156.  
  157.   procedure Compress(Plain : CodeType);
  158.     {-Compress a single character}
  159.   var
  160.     A : DownIndex;
  161.     U : UpIndex;
  162.     Sp : 0..MaxChar;
  163.     Stack : array[UpIndex] of Boolean;
  164.   begin
  165.     A := Plain+MaxChar;
  166.     Sp := 0;
  167.  
  168.     {Walk up the tree pushing bits onto stack}
  169.     repeat
  170.       U := Up[A];
  171.       Stack[Sp] := (Right[U] = A);
  172.       Inc(Sp);
  173.       A := U;
  174.     until A = Root;
  175.  
  176.     {Unstack to transmit bits in correct order}
  177.     repeat
  178.       Dec(Sp);
  179.       if Stack[Sp] then
  180.         OutByte := OutByte or BitMask[BitPos];
  181.       if BitPos = 7 then begin
  182.         {Byte filled with bits, write it out}
  183.         WriteByte;
  184.         BitPos := 0;
  185.         OutByte := 0;
  186.       end else
  187.         Inc(BitPos);
  188.     until Sp = 0;
  189.  
  190.     {Update the tree}
  191.     Splay(Plain);
  192.   end;
  193.  
  194.   procedure CompressFile;
  195.     {-Compress Inf, writing to OutF}
  196.   var
  197.     Header : FileHeader;
  198.   begin
  199.     {Write header to output}
  200.     Header.Signature := Sig;
  201.     BlockWrite(OutF, Header, SizeOf(FileHeader));
  202.  
  203.     {Compress file}
  204.     OutSize := 0;
  205.     BitPos := 0;
  206.     OutByte := 0;
  207.     repeat
  208.       BlockRead(InF, InBuffer, BufSize, InSize);
  209.       for Index := 1 to InSize do
  210.         Compress(InBuffer[Index]);
  211.     until InSize < BufSize;
  212.  
  213.     {Mark end of file}
  214.     Compress(EofChar);
  215.  
  216.     {Flush buffers}
  217.     if BitPos <> 0 then
  218.       WriteByte;
  219.     FlushOutBuffer;
  220.   end;
  221.  
  222.   procedure ReadHeader;
  223.     {-Read a compressed file header}
  224.   var
  225.     Header : FileHeader;
  226.   begin
  227.     BlockRead(InF, Header, SizeOf(FileHeader));
  228.     if Header.Signature <> Sig then begin
  229.       WriteLn('Unrecognized file format');
  230.       Halt(1);
  231.     end;
  232.   end;
  233.  
  234.   function GetByte : Byte;
  235.     {-Return next byte from compressed input}
  236.   begin
  237.     Inc(Index);
  238.     if Index > InSize then begin
  239.       {Reload file buffer}
  240.       BlockRead(InF, InBuffer, BufSize, InSize);
  241.       Index := 1;
  242.       {End of file handled by special marker in compressed file}
  243.     end;
  244.     {Get next byte from buffer}
  245.     GetByte := InBuffer[Index];
  246.   end;
  247.  
  248.   function Expand : CodeType;
  249.     {-Return next character from compressed input}
  250.   var
  251.     A : DownIndex;
  252.   begin
  253.     {Scan the tree to a leaf, which determines the character}
  254.     A := Root;
  255.     repeat
  256.       if BitPos = 7 then begin
  257.         {Used up the bits in current byte, get another}
  258.         InByte := GetByte;
  259.         BitPos := 0;
  260.       end else
  261.         Inc(BitPos);
  262.       if InByte and BitMask[BitPos] = 0 then
  263.         A := Left[A]
  264.       else
  265.         A := Right[A];
  266.     until A > PredMax;
  267.  
  268.     {Update the code tree}
  269.     Dec(A, MaxChar);
  270.     Splay(A);
  271.  
  272.     {Return the character}
  273.     Expand := A;
  274.   end;
  275.  
  276.   procedure ExpandFile;
  277.     {-Uncompress the input file and write output}
  278.   begin
  279.     {Force buffer load first time}
  280.     Index := 0;
  281.     InSize := 0;
  282.     {Nothing in output buffer}
  283.     OutSize := 0;
  284.     {Force bit buffer load first time}
  285.     BitPos := 7;
  286.  
  287.     {Read and expand the compressed input}
  288.     OutByte := Expand;
  289.     while OutByte <> EofChar do begin
  290.       WriteByte;
  291.       OutByte := Expand;
  292.     end;
  293.  
  294.     {Flush the output}
  295.     FlushOutBuffer;
  296.   end;
  297.  
  298.   procedure GetParameters;
  299.     {-Interpret command line parameters}
  300.   var
  301.     Arg : string[127];
  302.   begin
  303.     InName := '';
  304.     OutName := '';
  305.     CompressFlag := True;
  306.  
  307.     if ParamCount < 2 then begin
  308.       Write('Input file : ');
  309.       ReadLn(InName);
  310.       Write('Output file: ');
  311.       ReadLn(OutName);
  312.       Write('Expand? (Y/N) ');
  313.       ReadLn(CompStr);
  314.       if (Length(CompStr) = 1) and (Upcase(CompStr[1]) = 'Y') then
  315.         CompressFlag := False;
  316.     end else
  317.       for Index := 1 to ParamCount do begin
  318.         Arg := ParamStr(Index);
  319.         if (Arg[1] = '/') and (Length(Arg) = 2) then
  320.           case Upcase(Arg[2]) of
  321.             'X' : CompressFlag := False;
  322.           else
  323.             WriteLn('Unknown option: ', Arg);
  324.             Halt(1);
  325.           end
  326.         else if Length(InName) = 0 then
  327.           InName := Arg
  328.         else if Length(OutName) = 0 then
  329.           OutName := Arg
  330.         else begin
  331.           WriteLn('Too many filenames');
  332.           Halt(1);
  333.         end;
  334.       end;
  335.  
  336.     if Length(InName) = 0 then
  337.       Halt;
  338.     if Length(OutName) = 0 then
  339.       Halt;
  340.   end;
  341.  
  342. begin
  343.   GetParameters;
  344.   InitializeSplay;
  345.  
  346.   Assign(InF, InName);
  347.   Reset(InF, 1);
  348.   Assign(OutF, OutName);
  349.   Rewrite(OutF, 1);
  350.  
  351.   if CompressFlag then
  352.     CompressFile
  353.   else begin
  354.     ReadHeader;
  355.     ExpandFile;
  356.   end;
  357.  
  358.   Close(InF);
  359.   Close(OutF);
  360. end.
  361.